//These are changes made by Joshua Lawless
//I'm going to add some class files that will 
//allow a 3-D model to represent knee movement

import java.io.IOException;
import java.io.*;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.eclipse.swt.*;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.layout.*;

public class Kpodgui {

	private final Display display;
	private final Shell shell;
	private Label labelAccel1, labelAccel2, labelSession, statusBar, labelDX, labelDY;
	private Combo comboAccel1, comboAccel2;
	private Text textSession, textDX, textDY;
	private Table tableLog;
	private Button buttonStart, buttonClose, buttonGraphics;
	private MainEvent mainEvent;
	private Menu menuBar, fileMenu;
	private MenuItem fileMenuHeader, fileSettingsItem, fileExitItem; 
	private BufferedWriter output;
	/**
	 * @param args
	 */
	public static void main(String[] args) 
	{
		new Kpodgui();
	}

	private void setupGui()
	{
		// Setting shell details and preparing form layout
		FormLayout layout = new FormLayout();
		layout.marginHeight = 5;
		layout.marginWidth = 5;
		shell.setLayout( layout );
		shell.setText("kPod Data Analyzer");
		shell.setSize( 310, 580 );
		
		shell.addListener( SWT.Dispose, new Listener() {
			public void handleEvent( Event event ) {
				int style = SWT.APPLICATION_MODAL | SWT.YES | SWT.NO;
				MessageBox messageBox = new MessageBox( shell, style );
				messageBox.setText( "Information" );
				messageBox.setMessage( "Close the program?" );
				event.doit = messageBox.open() == SWT.YES;
			}
		});
		// Configuring menu bar
		menuBar = new Menu(shell, SWT.BAR);
		fileMenuHeader = new MenuItem(menuBar, SWT.CASCADE);
		fileMenuHeader.setText("&File");
		
		fileMenu = new Menu(shell, SWT.DROP_DOWN);
		fileMenuHeader.setMenu(fileMenu);
		
		fileSettingsItem = new MenuItem( fileMenu, SWT.PUSH);
		fileSettingsItem.setText( "&Settings");
		
		fileExitItem = new MenuItem( fileMenu, SWT.PUSH);
		fileExitItem.setText("E&xit");
		
		shell.setMenuBar( menuBar );
		
		// Label that acts as a status bar because SWT doesn't have a status bar
		// widget.
		statusBar = new Label(shell, SWT.BORDER | SWT.RIGHT );
		statusBar.setText( "Application Ready" );
		
		// Label for Accelerometer 1 drop down
		labelAccel1 = new Label(shell, SWT.NONE);
		labelAccel1.setText("Port 1:");
		
		// Accelerometer 1 drop down menu
		comboAccel1 = new Combo(shell, SWT.DROP_DOWN | SWT.READ_ONLY );
		
		// Accelerometer 2 label
		labelAccel2 = new Label(shell, SWT.NONE);
		labelAccel2.setText("Port 2:");
		
		// Accelerometer 2 drop down
		comboAccel2 = new Combo(shell, SWT.DROP_DOWN | SWT.READ_ONLY );
		
		// Label for the session field to give a session name
		labelSession = new Label(shell, SWT.NONE);
		labelSession.setText("Session Profile");

		// Field to enter in the name of a session for logging
		textSession = new Text( shell, SWT.BORDER );
		textSession.setText( "Default" );
		
		// Fields to display the difference between X and Y
		labelDX = new Label( shell, SWT.NONE );
		labelDX.setText( "dX:" );
		labelDY = new Label( shell, SWT.None );
		labelDY.setText( "dY:" );
		textDX = new Text( shell, SWT.BORDER );
		textDY = new Text( shell, SWT.BORDER );
		
		// The table that displays data being pulled from devices
		tableLog = new Table(shell, SWT.BORDER | SWT.V_SCROLL );
		tableLog.setLinesVisible(true);
		tableLog.setHeaderVisible(true);
		
		// Setting up table columns
		//TableColumn dateTime = new TableColumn(tableLog, SWT.LEFT);
		//dateTime.setText("Date and Time");
		//dateTime.setWidth(115);
		TableColumn colAccel1 = new TableColumn(tableLog, SWT.LEFT);
		colAccel1.setText("Accelerometer 1");
		colAccel1.setWidth(135);
		TableColumn colAccel2 = new TableColumn(tableLog, SWT.LEFT);
		colAccel2.setText("Accelerometer 1");
		colAccel2.setWidth(138);
		
		// Start logging button
		buttonStart = new Button(shell, SWT.PUSH);
		buttonStart.setText("Start");
		buttonStart.addSelectionListener( new SelectionAdapter() {
			public void widgetSelected(SelectionEvent e) {
				if( buttonStart.getText() == "Start" )
				{
					buttonStart.setText( "Stop" );
					if( mainEvent == null )
					{
						mainEvent = new MainEvent();
						try
						{
							output = new BufferedWriter( new FileWriter( new File( textSession.getText() + ".txt" ) ));
						} catch( IOException ex ) { ex.printStackTrace(); }
						mainEvent.start();
					}
					else
					{
						mainEvent.startCollecting();
					}
				}
				else
				{
					buttonStart.setText( "Start" );
					mainEvent.stopCollecting();
				}
			}
		});
		
		// 3D Graphics program button
		buttonGraphics = new Button(shell, SWT.PUSH);
		buttonGraphics.setText("3D Model");
		buttonGraphics.addSelectionListener( new SelectionAdapter() {
			public void widgetSelected( SelectionEvent e) {
				KpodGraphics threeDimModel = new KpodGraphics();
				threeDimModel.run();
			}
		});
		
		// Close program button
		buttonClose = new Button(shell, SWT.PUSH);
		buttonClose.setText("Close");
		buttonClose.addSelectionListener( new SelectionAdapter() {
			public void widgetSelected( SelectionEvent e ) {
				shell.close();
			}
		});
		
		// Set widgets with layout information
		
		// Drop down for selecting port 1
		FormData data = new FormData();
		data.top = new FormAttachment(comboAccel1, 5, SWT.CENTER);
		labelAccel1.setLayoutData( data );
		data = new FormData();
		data.left = new FormAttachment( labelAccel1, 5 );
		comboAccel1.setLayoutData(data);
		
		// Drop down for selecting port 2
		data = new FormData();
		data.top = new FormAttachment( comboAccel2, 0, SWT.CENTER );
		data.left = new FormAttachment( comboAccel1, 5 );
		labelAccel2.setLayoutData( data );
		data = new FormData();
		data.left = new FormAttachment( labelAccel2, 5 );
		comboAccel2.setLayoutData( data );
		
		// Sets name for logging session (names the file)
		data = new FormData();
		data.top = new FormAttachment( textSession, 0, SWT.CENTER );
		labelSession.setLayoutData( data );
		data = new FormData();
		data.top = new FormAttachment( labelAccel1, 20, SWT.BOTTOM );
		data.left = new FormAttachment( labelSession, 5 );
		data.width = 100;
		textSession.setLayoutData( data );
		
		// Table for input from accelerometers
		data = new FormData();
		data.top = new FormAttachment( labelSession, 10, SWT.BOTTOM | SWT.CENTER );
		data.height = 290;
		tableLog.setLayoutData( data );
		
		// The start button
		data = new FormData();
		data.top = new FormAttachment( tableLog, 15, SWT.BOTTOM );
		data.right = new FormAttachment( 100, 0 );
		data.width = 80;
		buttonStart.setLayoutData( data );
		
		// The close button
		data = new FormData();
		data.top = new FormAttachment( buttonStart, 5, SWT.BOTTOM );
		data.right = new FormAttachment( 100, 0 );
		data.width = 80;
		buttonClose.setLayoutData( data );
		
		// The 3D Model button
		FormData modelData = new FormData();
		modelData.top = new FormAttachment( buttonClose, 5, SWT.BOTTOM );
		modelData.right = new FormAttachment( 100, 0);
		modelData.width = 80;
		buttonGraphics.setLayoutData( modelData );
		
		// Status bar at the bottom of the window
		data = new FormData();
		data.bottom = new FormAttachment( 100, 0 );
		data.left = new FormAttachment( 0, 0 );
		data.right = new FormAttachment( 100, 0 );
		statusBar.setLayoutData( data );
		
		// Difference in X values
		data = new FormData();
		data.top = new FormAttachment( textDX, 5, SWT.CENTER );
		labelDX.setLayoutData( data );
		data = new FormData();
		data.top = new FormAttachment( tableLog, 15, SWT.BOTTOM );
		data.left = new FormAttachment( labelDX, 5 );
		textDX.setLayoutData( data );
		
		// Difference in Y values
		data = new FormData();
		data.top = new FormAttachment( textDY, 5, SWT.CENTER );
		labelDY.setLayoutData( data );
		data = new FormData();
		data.top = new FormAttachment( textDX, 10, SWT.BOTTOM );
		data.left = new FormAttachment( labelDY, 5 );
		textDY.setLayoutData( data );
	}
	
	public Kpodgui() 
	{	
		display = new Display();
		shell = new Shell(display, SWT.CLOSE | SWT.TITLE | SWT.MIN );
		setupGui();
		
		/* This thread starts immediately.  Previously, I put this in the main
		 * method.  This caused the program to hang as it queried for available
		 * comm ports.  This allows the GUI to show up and populate the com ports
		 * after the GUI is already running.  Probably want some sort of splash
		 * screen or a status bar to let the user know what's going on
		 */
		populateCommPorts();

		// After widgets are defined and configured, shell must be opened
		shell.open();
		while(!shell.isDisposed())
		{
			// This is the event loop.  When shell (window) is closed,
			// the loop will be exited.
			if( !display.readAndDispatch())
			{
				display.sleep();
			}
		}
		System.out.println( "Shell disposed of" );
		mainEvent.closePorts();
		// When the shell is closed, the display must be disposed
		display.dispose();
	}
	
	private void populateCommPorts()
	{
		new Thread() {
			public void run() {
				final String[] items = Kpodconnection.getAvailableSerialPorts();
				display.asyncExec(new Runnable() {
					public void run() {
						if( items != null )
						{
							comboAccel1.setItems( items );
							comboAccel1.select( 0 );
							comboAccel2.setItems( items );
							comboAccel2.select( 0 );
						}
					}
				});
			}
		}.start();
	}
	
	public class MainEvent extends Thread
	{
		private boolean closed = false;
		private boolean endThread = false;
		private Kpodconnection accel1;
		private Kpodconnection accel2;

		private void connectPorts()
		{
			accel1 = new Kpodconnection();
			accel2 = new Kpodconnection();
			final String[] names = new String[2];
			
			display.syncExec( new Runnable() {
				public void run() 
				{
					names[0] = comboAccel1.getText();
					names[1] = comboAccel2.getText();
				}
			});
			
			accel1.setPortName( names[0] );
			accel2.setPortName( names[1] );
			accel1.setDebug( true );
			accel2.setDebug( true );
			
			accel1.openConnection();
			accel2.openConnection();
			
			try
			{
				accel1.sendString( "1" );
				accel2.sendString( "1" );
			}
			catch( IOException e ) { e.printStackTrace(); }
		}
		
		public void stopCollecting()
		{
			closed = true;
			try
			{
				accel1.sendString( "S" );
				accel2.sendString( "S" );
			}
			catch( IOException e ) { System.out.println( "IOException error " ); }
		}
		
		public boolean isCollecting()
		{
			return !closed;
		}
		
		public void startCollecting()
		{
			accel1.clearMessage();
			accel2.clearMessage();
			
			try
			{
				output.write( "Time Stamp,X1,Y1,R1,X2,Y2,R2,dX,dY" );
				output.newLine();
			} catch( IOException e ) { e.printStackTrace(); }
			
			display.asyncExec( new Runnable() 
			{
				public void run() 
				{
					tableLog.removeAll();
				}
			});
			
			closed = false;
			try
			{
				accel1.sendString( "1" );
				accel2.sendString( "1" );
			}
			catch( IOException e ) { e.printStackTrace(); }
		}
		
		public void closePorts()
		{
			endThread = true;
			stopCollecting();
			accel1.closeConnection();
			accel2.closeConnection();
		}
		
		public void run() 
		{
			connectPorts();
			try
			{
				output.write( "Time Stamp,X1,Y1,R1,X2,Y2,R2,dX,dY" );
				output.newLine();
			} catch( IOException e ) { e.printStackTrace(); }
			
			while( true )
			{
				if( isCollecting() || !accel1.isEmpty() )
				{
					if( !accel1.isEmpty() )
					{
						if( !accel2.isEmpty() )
						{
							Object[] obj1 = accel1.getMessage();
							Object[] obj2 = accel2.getMessage();
							
							final Date date = (Date)obj1[0];
							final String message1 = (String)obj1[1];
							final String message2 = (String)obj2[1];
							String pattern = "X=(\\d{3}\\.\\d)Y=(\\d{3}\\.\\d)R=((-)?\\d{3})(B=\\d\\.\\d{3})?";
							Pattern r = Pattern.compile(pattern);
							Matcher m = r.matcher(message1);
							m.find();
							float x1 = Float.valueOf( m.group(1).trim() ).floatValue();
							float y1 = Float.valueOf( m.group(2).trim() ).floatValue();
							float r1 = Float.valueOf( m.group(3).trim() ).floatValue();
							
							m = r.matcher( message2 );
							m.find();
							float x2 = Float.valueOf( m.group(1).trim() ).floatValue();
							float y2 = Float.valueOf( m.group(2).trim() ).floatValue();
							float r2 = Float.valueOf( m.group(3).trim() ).floatValue();
							
							final float dx, dy;
							
							dx = Math.abs( x1 - x2 );
							dy = Math.abs( y1 - y2 );
							
							try
							{
								output.write( "\"T" + date.getTime() + "\"," + Float.toString( x1 ) + "," + Float.toString( y1 ) + "," + Float.toString( r1 ) );
								output.write( "," + Float.toString( x2 ) + "," + Float.toString( y2 ) + "," + Float.toString( r2 ));
								output.write( "," + Float.toString( dx ) + "," + Float.toString( dy ));
								output.newLine();
							} catch( IOException e ) { e.printStackTrace(); }
							
							display.asyncExec( new Runnable() 
							{
								public void run() 
								{
									TableItem item;
									item = new TableItem(tableLog, SWT.NONE);
									item.setText(new String[] { message1, message2 });
									tableLog.showItem( item );
									textDX.setText( Float.toString( dx ) );
									textDY.setText( Float.toString( dy ) );
								}
							});
						}
						else
						{
							try
							{
								Thread.sleep(1);
							} catch( InterruptedException e ) { continue; }
						}
					}
					else
					{
						try
						{
							Thread.sleep(1);
						}
						catch( InterruptedException e ) { continue; }
					}
				}
				else
				{
					if( output != null )
					{
						try
						{
							output.close();
							output = null;
						} catch( IOException e ) { e.printStackTrace(); }
					}
					
					if( endThread )
					{
						break;
					}
					else
					{
						try
						{
							Thread.sleep(10);
						} catch( InterruptedException e ) { break; }
					}
				}
			}
		}
	}
}
